AWS IoT の Device Shadow を iOS アプリから MQTT で使ってみた #reinvent
はじめに
AWS re:Invent 2015 で発表された AWS IoT は IoT (モノのインターネット) のためのマネージド型のクラウドプラットフォームを提供してくれるサービスです。現在はベータ版として一般公開されています(2015/10/26現在)。
AWS IoT の機能の一つである「Device Shadow」を使うと、IoT デバイスの状態を管理することができます。IoT デバイスのセンサー情報を受け取ったり、逆に IoT デバイスの状態を変更するためにデータを送ったり、双方向にやり取りすることができます。この通信には MQTT または HTTPS が利用できます。
AWS IoT を試したい!しかしながら「IoT デバイスは持っていない」「コマンドラインは苦手…」という人も少なくないと思います。そこで、この記事では「AWS IoT の Device Shadow を iOS アプリから使ってみる」ということを試してみたいと思います。
Mac と Xcode、そしてインターネットがあれば、誰でもできます!
Thing の作成
まずは iPhone を Thing として AWS IoT に登録しましょう。マネジメントコンソールから「Create a thing」を選び、名前を「iPhone-5s」とします。
「View thing」を押すと、エンドポイントや Device Shadow 用の MQTT トピックなどが確認できます。これらはあとで使います。
次にポリシーと証明書を作成し、証明書をダウンロードします。「Connect a Device」をクリックしましょう。
「Embedded C」を選択し、証明書を生成します。作成が終わると Public キー、Private キー、証明書ファイルがダウンロード可能となります。今回は Private キー、証明書ファイルをダウンロードしておきます。
これで AWS IoT 側の準備は終わりです。なお、ここまでの操作はコマンドラインからも行えます。コマンドラインから行う方法は、次の記事を参考にしてください。
iOS アプリの実装
次に、iOS アプリを実装しましょう。このアプリでは、現在の位置情報を MQTT トピックに Publish してみたいと思います。なお、今回は Swift で書きます。
iOS 向け MQTT クライアントライブラリについて
iOS で利用可能な MQTT クライアントライブラリはいくつかありますが、ほぼ mosquito のラッパーです。その中から今回は Moscapsule を使用しました。こちらを使うと X.509 形式の証明書を利用した MQTT 通信が行えます。OpenSSL-Universal も依存しているので、こちらも一緒にインポートします。
また、Device Shadow に JSON データを送る必要があるため、JSON を簡単に扱うため SwiftyJSON もインポートします。
use_frameworks! target 'MQTTSample' do pod 'Moscapsule', :git => 'https://github.com/flightonary/Moscapsule.git' pod 'OpenSSL-Universal', '~> 1.0.1.l' pod 'SwiftyJSON' end
証明書のインポート
次に、Xcode プロジェクトに先ほどダウンロードしてきた証明書と Private キーファイルをインポートします。また、それらに加えて ルートCA ファイルも必要です。ルートCA ファイルはシマンテックのサイトからダウンロードしてインポートしておきます。ここでは ca.pem
という名前にしています。
ちなみに Xcode では証明書ファイルの情報を閲覧することができます。AWS から発行されていること、そして2050年まで有効であることが確認できます。
Device Shadow の Topic に向けた Publish
ということで実装です。まずは View Controller に CoreLocation を使った位置情報の取得を実装し、locationManager:didUpdateLocations:
メソッドで取得できた位置情報を Device Shadow に向けて Publish します。位置情報を利用するときは Info.plist
に NSLocationWhenInUseUsageDescription
を登録しないといけないので、忘れずに追加してください。
import UIKit import Moscapsule import SwiftyJSON import CoreLocation class ViewController: UIViewController, CLLocationManagerDelegate { let manager = CLLocationManager() var mqttClient: MQTTClient? override func viewDidLoad() { super.viewDidLoad() initMQTTClient() self.manager.delegate = self self.manager.requestWhenInUseAuthorization() } func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) { if status == .AuthorizedWhenInUse { manager.startUpdatingLocation() } } // 位置情報の更新で MQTT のトピックに送信 func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { let location = locations.first! let json: JSON = [ "state": [ "reported": [ "location": [ "latitude": location.coordinate.latitude, "longitude": location.coordinate.longitude ] ] ] ] publishTopic(json) } // MQTT クライアントの初期設定 func initMQTTClient() { moscapsule_init() let mqttConfig = MQTTConfig(clientId: "server_cert_test", host: "XXXXXXXXXXXXXX.iot.ap-northeast-1.amazonaws.com", port: 8883, keepAlive: 60) let certFile = NSBundle.mainBundle().pathForResource("xxxxxxxxxx-certificate.pem", ofType: "crt") let keyFile = NSBundle.mainBundle().pathForResource("xxxxxxxxxx-private.pem", ofType: "key") let caFile = NSBundle.mainBundle().pathForResource("ca", ofType: "pem") mqttConfig.mqttServerCert = MQTTServerCert(cafile: caFile, capath: nil) mqttConfig.mqttClientCert = MQTTClientCert(certfile: certFile!, keyfile: keyFile!, keyfile_passwd: nil) self.mqttClient = MQTT.newConnection(mqttConfig) } // Device Shadow トピックに向けた Publish func publishTopic(json: JSON) { let data = try! json.rawData() self.mqttClient!.publish(data, topic: "$aws/things/iPhone-5s/shadow/update", qos: 2, retain: false) } }
特記すべきは initMQTTClient
メソッドの中身です。MQTT の設定は MQTTConfig
クラスで行いますが、証明書やら Private キーやらを適切に設定してあげる必要があります。
MQTTServerCert
にはルート CA ファイルと呼ばれる証明書を設定する必要があります。これは先ほどシマンテックからダウンロードした ca.pem
ですね。capath
は nil
で結構です。
MQTTClientCert
には証明書ファイルと Private キーファイルを設定します。証明書ファイルは AWS IoT のマネジメントコンソールからダウンロードしてきた xxxxxxxxxx-certificate.pem
という形式のファイル、Private キーのファイルは xxxxxxxxxx-private.pem
という形式のファイルをそれぞれ設定します。
試してみる
アプリを実行してみましょう。次のようなログがコンソールで出れば Publish できています。
2015-10-26 00:31:49.878 MQTTSample[23715:1595292] [MOSQUITTO] DEBUG Client server_cert_test sending CONNECT 2015-10-26 00:31:50.262 MQTTSample[23715:1595292] [MOSQUITTO] DEBUG Client server_cert_test received CONNACK 2015-10-26 00:32:50.261 MQTTSample[23715:1595292] [MOSQUITTO] DEBUG Client server_cert_test sending PINGREQ 2015-10-26 00:32:50.262 MQTTSample[23715:1595292] [MOSQUITTO] DEBUG Client server_cert_test sending PUBLISH (d1, q2, r0, m1, '$aws/things/iPhone-5s/shadow/update', ... (86 bytes)) 2015-10-26 00:32:50.262 MQTTSample[23715:1595292] [MOSQUITTO] DEBUG Client server_cert_test sending PUBLISH (d1, q2, r0, m2, '$aws/things/iPhone-5s/shadow/update', ... (86 bytes)) 2015-10-26 00:32:50.262 MQTTSample[23715:1595292] [MOSQUITTO] DEBUG Client server_cert_test sending PUBLISH (d1, q2, r0, m3, '$aws/things/iPhone-5s/shadow/update', ... (86 bytes)) ...
AWS IoT で様子を見てみます。
バッチリ送信できました!
まとめ
気軽に試したい一心で、iOS アプリから試してみました。Android アプリでやる場合は Paho などを使うと良さそうです。
この記事のアイデアは こちらのセッション から来ているものですが、IoT デバイスを遠隔操作したり、遠隔で状態を確認したりするときに使えると思います。また、IoT デバイスを気軽にシミュレートする用途でもお使い頂けると思います。
自分の iPhone から、IoT を体験してみましょう!参考になれば幸いです。